package 其他;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/*
Java中的回调函数

1.定义
在计算机程序设计中，回调函数，或简称回调（Callback 即call then back 被主函数调用运算后会返回主函数），是指通过函数参数传递到其它代码的，某
一块可执行代码的引用。这一设计允许了底层代码调用在高层定义的子程序。


在java的回调函数，以类A调用类B方法为例
在类A中方法a调用了类B中的b方法，b方法在执行完成之后，会执行类A中的callback()方法，这个callback方法就是回调函数，表明类B的b方法执行完成。
   A.class               B.class
   a() --------------->  b()
   callback()<------------|

2.同步回调
同步回调的话会等待回调函数执行完成之后才执行其他的业务。

以儿子要坐火车出远门，跟妈妈告别，到了学校之后打电话告诉自己到学校这件事为例：

接口：

public interface Callable {
    //回调函数
    public void call();
}

妈妈：

//定义类Mother实现了Callable接口，实现了call()回调函数的具体内容
public class Mother implements Callable {
    Son son;
    public Mother(Son son){
        this.son=son;
    }
    //表示妈妈和儿子的分别函数，儿子在这期间搭乘火车离开
    public void parting(){
       System.out.println("开始执行同步回调函数");
       son.rideTrain(this);
       System.out.println("同步回调函数执行完成");
    }
    @Override
    public void call() {
        System.out.println("儿子到学校了");
    }
}

儿子：

public class Son {
    public void rideTrain(Callable callable){
        try {
           //模拟坐火车
            Thread.sleep(5000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        callable.call();//到了打电话给妈妈
    }

测试类：

public class Test {
    public static void main(String[] args) {
        Son jack=new Son();
        Mother mother=new Mother(jack);
        mother.parting();
    }
}

运行结果：

开始执行同步回调函数
儿子到学校了
同步回调函数执行完成

3 异步回调
异步调用发生在某些业务十分耗时，又需要立即返回结果，这个时候需要开辟新的线程处理耗时业务，业务处理完了，再去回调回调函数，处理其他业务。

还是以妈妈和儿子为例,区别是妈妈中的parting函数不同实现，开启了线程处理函数。

public class Mother implements Callable {

    Son son;

    public Mother(Son son){
        this.son=son;
    }

    public void parting(){
        System.out.println("开始执行异步回调函数");
        new Thread(new Runnable() {
            @Override
            public void run() {
                son.rideTrain(Mother.this);
            }
        }).start();
        System.out.println("异步回调函数执行完成");//开启线程处理儿子坐火车的函数，这条打印语句会在回调函数执行完成前执行。
    }

    @Override
    public void call() {
        System.out.println("儿子到学校了");
    }
}

最后的运行结果：

开始执行异步回调函数
异步回调函数执行完成
儿子到学校了

4 总结
1) 回调函数可以用于事件监听，当发生相对应的事件时系统可以自动调用对应的函数
2) 回调函数可以用于中断处理。


在计算机程序设计中，回调函数，简称回调（Callback），是指通过函数参数传递到其他代码的，某一块可执行代码的引用。这一设计允许了底层代码调用在高层定义的子程序。
Java 实现回调的四种写法：

·反射；
·直接调用；
·接口调用；
·Lambda表达式。

在开始之前，先介绍下本文代码示例的背景，在 main 函数中，我们异步发送一个请求，并且指定处理响应的回调函数，接着 main 函数去做其他事，而当响应到达后，执行回调函数。

2. 反射
Java 的反射机制允许我们获取类的信息，其中包括类的方法。我们将以 Method 类型去获取回调函数，然后传递给请求函数。示例如下：

Request 类中的 send 方法有两个参数 clazz、method，分别是Class 类型和 Method 类型，这里的 method 参数就是待传入的回调函数，而为了通过 invoke 方法进行反射调用，还需要一个实例，所以将回调函数所在的类的 Class 对象作为参数传递进来，通过 newInstance 构造一个对象，将顺利通过 invoke 反射调用。
*/
/*
class Request{
    public void send(Class clazz, Method method) throws Exception {
        // 模拟等待响应
        Thread.sleep(3000);
        System.out.println("[Request]:收到响应");
        method.invoke(clazz.newInstance());
        //method.invoke(clazz.getConstructor().newInstance());
    }
}

//CallBack 类很简单，只有一个 processResponse 方法，用于当作回调函数，处理响应。

class CallBack {
    public void processResponse() {
        System.out.println("[CallBack]:处理响应");
    }
}

//我们在 main 方法中，新开了一个线程去发送请求，并且把需要的 CallBack.class 和 processResponse 方法传递进去。

//public class Main {
public class 回调函数 {

    public static void main(String[] args) throws Exception {
        Request request = new Request();
        System.out.println("[Main]:我开个线程去异步发请求");
        new Thread(() -> {
            try {
                request.send(CallBack.class, CallBack.class.getMethod("processResponse"));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
        System.out.println("[Main]:请求发完了，我去干点别的");
        Thread.sleep(100000);
    }
}
*/
/*
Output:
[Main]:我开个线程去异步发请求
[Main]:请求发完了，我去干点别的
[Request]:收到响应
[CallBack]:处理响应


这种写法需要传递的参数十分繁琐，所以不推荐。下面介绍简单的直接调用写法。

3. 直接调用
我们来改写下 send 方法的参数，改为一个 CallBack 类型参数。如下：

在 send 方法中我们不使用反射，改为直接通过对象来调用方法。

public class Request{
    public void send(CallBack callBack) throws Exception {
        // 模拟等待响应
        Thread.sleep(3000);
        System.out.println("[Request]:收到响应");
        callBack.processResponse();
    }
}

main 函数中，我们 new 了一个 CallBack 对象作为参数传递给 send 方法。

public class Main {
    public static void main(String[] args) throws Exception {
        Request request = new Request();
        System.out.println("[Main]:我开个线程去异步发请求");
        CallBack callBack = new CallBack();
        new Thread(() -> {
            try {
                request.send(callBack);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
        System.out.println("[Main]:请求发完了，我去干点别的");
        Thread.sleep(100000);
    }
}

这种实现方式十分简单，但是存在的问题是不符合修改封闭原则。也就是说当我们想要换一种“处理响应”的方法时，将必须去修改 CallBack 类
的 processRequest()方法。而如果将 CallBack 类改为接口，我们就可以仅更换 CallBack 的实现了。下面请看接口调用的写法。

4. 接口调用
首先将 CallBack 类改为接口。

public interface CallBack {
    public void processResponse();
}

再新增一个 CallBack 接口的实现类 CallBackImpl。

public class CallBackImpl implements CallBack {
    @Override
    public void processResponse() {
        System.out.println("[CallBack]:处理响应");
    }
}

Request 类不变。Main 类中的 main 方法将实例化一个 CallBackImpl，然后通过 CallBack 接口传递进去。

public class Main {
    public static void main(String[] args) throws Exception {
        Request request = new Request();
        System.out.println("[Main]:我开个线程去异步发请求");
        CallBack callBack = new CallBackImpl();
        new Thread(() -> {
            try {
                request.send(callBack);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
        System.out.println("[Main]:请求发完了，我去干点别的");
        Thread.sleep(100000);
    }
}

5. Lambda表达式
上述方法已经介绍的差不多了，最后我们再介绍一种更加简洁的写法，通过使用 Lamda 表达式，将不用新增一个 CallBack 接口的实现类。下面请看改写的 main 方法：

public class Main {
    public static void main(String[] args) throws Exception {
        Request request = new Request();
        System.out.println("[Main]:我开个线程去异步发请求");
        new Thread(() -> {
            try {
                request.send(()-> System.out.println("[CallBack]:处理响应"));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
        System.out.println("[Main]:请求发完了，我去干点别的");
        Thread.sleep(100000);
    }
}
我们既不用去新增实现类，也不用去实例化，只需要传递 Lambda 表达式就可以完成回调了。

6. 总结
为了让大家更好的理解回调，本文一共介绍了 4 种写法，除了反射不推荐，其他三种都可以根据自己的需要自取。
*/
/*
应用场景如下：

A类在内存中维护了一组词表，A类的定义如下：
*/
/*
class A {
	public List<String>  wordList=loadList();
	public <T> T execute (CallBackInterface callBackInterface) {
		//可以先执行一些execute的逻辑
		//直接将相关词库的操作权都交给了callBackInterface
		T result =(T) callBackInterface.process(wordList);
		return result;
	}
	//加载词库到内存中
	public List<String> loadList(){
		List<String> wordList=new ArrayList<>();
		for (int i=0;i<10;i++) {
			wordList.add(Integer.toString(i));
		}
		return wordList;
	}
}

//定义一个泛形接口，对于返回值和参数都十分的灵活：
interface CallBackInterface<T>{
	T process(List<Object> param);
}

//应用场景：
public class 回调函数{
	public static void main(String[] args) throws Exception {
		A a=new A();
		//
		List<String> result1=a.execute(new CallBackInterface() {
			public Object process(List param) {
				List<String> wordList=param;
				wordList.remove("1");
				return wordList;
			}
		});
		List<String> result2=a.execute(new CallBackInterface() {
			public Object process(List param) {
				List<String> wordList=param;
				wordList.add("24");
				wordList.add("34");
				//return true;
				return wordList;
			}
		});
		System.out.println(result1);
		System.out.println(result2);
	}
}
*/
/*
好了，上面简单的三个类就是一个回调的应用，所谓的回调函数就是process函数这个函数是被传入后又被调用的，就我自己的理解而言回调有以下优点：
1、非常的灵活，用户可以在匿名内部类中定义自己的实现方法。
2、回调相当于c++中的参数里的函数指针，可以在实现了CallbackInterface接口的类中，或者匿名内部类中改变其他类中的成员变量。
3、回调还出现在button中的监听器里，安卓代码中形式如下：
4、其实定义一个新的线程然后在run方法中实现相应的逻辑也是一种回调。
5、回调的概念其实不难，难在怎么在设计中灵活的运用
*/
/*
在看Handler时看到了这么语段代码顿时就蒙了

Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
	(MethodIntrospector.MetadataLookup<T>) method -> {
		try {
			return getMappingForMethod(method, userType);
		}
		catch (Throwable ex) {
			throw new IllegalStateException("Invalid mapping on handler class [" +
					userType.getName() + "]: " + method, ex);
		}
    });

经过指点知道这是回调函数，马上来补充一下~

什么是回调函数
回调函数，是一个通过函数指针调用的函数。如果你把函数的指针（地址）作为参数传递给另一个函数，当这个指针被用来调用其所指向的函数时，我们就说这是回调函数。
在Java中，指针即所谓的引用。回调函数不是由该函数的实现方直接调用，而是在特定的事件或条件发生时由另外的一方调用的，用于对该事件或条件进行响应。
第一次看这知识点，是真的懵。下面看我拆解后逐步理解它的思路。

1.第0个版本
初代版本
我调用了你，在我调用你的方法里你又调用了我（回调）
在类ClassA中方法a调用了类ClassB中的b方法，b方法在执行完成之后，会执行类ClassA中的backs()方法，这个backs方法就是回调函数，表明类ClassB的b方法执行完成。
   ClassA.class        ClassB.class
   a() --------------->  b()
   backs()<---------------|
*/
/*
class ClassA {
    public void a() {
        System.out.println("执行了a方法");
        ClassB b = new ClassB();
        b.b();
    }
    public void backs(){
        System.out.println("A:我就是A的回调函数！");
    }
}

class ClassB {
    public void b() {
        System.out.println("我执行了b");
        System.out.println("B:我开始调用A的回调-->");
        ClassA a = new ClassA();
        a.backs();
        System.out.println("B: <--我完成调用A的回调");
    }
}

public class 回调函数 {
    public static void main(String[] args) {
        ClassA a = new ClassA();
        a.a();
    }
}
*/
/*
执行结果

执行了a方法
我执行了b
B:我开始调用A的回调-->
A:我就是A的回调函数！
B: <--我完成调用A的回调

2.第1个版本
演变一下把在B里创建的A，用对象的形式在A里调用时就带过去。
写一个回调用的接口
*/
/*
class ClassA {
    public void a() {
        System.out.println("执行了a方法");
        ClassB b = new ClassB();
        b.b(this);//将ClassA的实例作为参数代入
    }
    public void backs(){
        System.out.println("A:我就是A的回调函数！");
    }
}


class ClassB {
    public void b(ClassA a) {
        System.out.println("我执行了b");
        System.out.println("B:我开始调用A的回调-->");
        a.backs();
        System.out.println("B: <--我完成调用A的回调");
    }
}
public class 回调函数 {//测试类，非回调函数
   public static void main(String[] args) {
       ClassA a = new ClassA();
       a.a();
   }
}
*/
/*
Main方法不用变
执行结果，执行结果不变

执行了a方法
我执行了b
B:我开始调用A的回调-->
A:我就是A的回调函数！
B: <--我完成调用A的回调

3.第2个版本
把第1个版本中的这个类换成接口Interface
创建一个接口
*/
/*
interface Interface {
    public void backs();
}

class ClassA {
    public void a() {
        System.out.println("执行了a方法");
        ClassB b = new ClassB();
//        b.b(new Interface() {
//            @Override
//            public void backs() {
//                System.out.println("A:我就是A的回调函数！");
//            }
//        });
        b.b(()->System.out.println("A:我就是A的回调函数！"));
    }
}

class ClassB {
    public void b(Interface in) {
        System.out.println("我执行了b");
        System.out.println("B:我开始调用A的回调-->");
        in.backs();
        System.out.println("B: <--我完成调用A的回调");
    }
}
public class 回调函数 {//测试类，非回调函数
   public static void main(String[] args) {
       ClassA a = new ClassA();
       a.a();
   }
}
*/
/*
Main依然不变
执行结果也不变

执行了a方法
我执行了b
B:我开始调用A的回调-->
A:我就是A的回调函数！
B: <--我完成调用A的回调

4.第3个版本
给接口加一个入参，让回调方法可以传参
*/
/*
interface Interface {
    public void backs(String n);
}

class ClassA {
    public void a() {
        System.out.println("执行了a方法");
        ClassB b = new ClassB();
//        b.b(new Interface() {
//            @Override
//            public void backs(String n) {
//                System.out.println("A:我就是A的回调函数！我打印：" + n);
//            }
//        });
        b.b(n->System.out.println("A:我就是A的回调函数！我打印：" + n));
    }
}

class ClassB {
    public void b(Interface in) {
        System.out.println("我执行了b");
        System.out.println("B:我开始调用A的回调-->");
        in.backs("《我是B传的参数》");
        System.out.println("B: <--我完成调用A的回调");
    }
}
public class 回调函数 {//测试类，非回调函数
   public static void main(String[] args) {
       ClassA a = new ClassA();
       a.a();
   }
}
*/
/*
执行结果

执行了a方法
我执行了b
B:我开始调用A的回调-->
A:我就是A的回调函数！我打印：《我是B传的参数》
B: <--我完成调用A的回调

5.第4个版本
给接口加个返回的参数
*/
/*
interface Interface {
    public String backs(String n);
}

class ClassA {
    public void a() {
        System.out.println("执行了a方法");
        ClassB b = new ClassB();
//        b.b(new Interface() {
//            @Override
//            public String backs(String n) {
//                System.out.println("A:我就是A的回调函数！我打印：" + n);
//                return "A:我就是A的回调函数！我打印：" + n + "的返回。";
//            }
//        });
        b.b(n->{
      	  System.out.println("A:我就是A的回调函数！我打印：" + n);
         return "A:我就是A的回调函数！我打印：" + n + "的返回。";
        });
    }
}

class ClassB {
    public void b(Interface in) {
        System.out.println("我执行了b");
        System.out.println("B:我开始调用A的回调-->");
        String backs = in.backs("《我是B传的参数》");
        System.out.println("B:我收到了回调的结果:"+backs);
        System.out.println("B: <--我完成调用A的回调");
    }
}
public class 回调函数 {//测试类，非回调函数
   public static void main(String[] args) {
       ClassA a = new ClassA();
       a.a();
   }
}
*/
/*
执行结果

执行了a方法
我执行了b
B:我开始调用A的回调-->
A:我就是A的回调函数！我打印：《我是B传的参数》
B:我收到了回调的结果:A:我就是A的回调函数！我打印：《我是B传的参数》的返回。
B: <--我完成调用A的回调

6.第5个版本
*/
/*
interface Interface {
    public String backs(String n);
}

class ClassA {
    public void a() {
        System.out.println("执行了a方法");
        ClassB b = new ClassB();
        String b1 = b.b(new Interface() {
            @Override
            public String backs(String n) {
                System.out.println("A:我就是A的回调函数！我打印：" + n);
                return "A:我就是A的回调函数！我打印：" + n + "的返回。";
            }
        });
        System.out.println("A:执行完得到的结果:" + b1);
    }
}

class ClassB {
    public String b(Interface in) {
        System.out.println("我执行了b");
        System.out.println("B:我开始调用A的回调-->");
        String backs = in.backs("《我是B传的参数》");
        System.out.println("B:我收到了回调的结果:"+backs + "<--我完成调用A的回调");
        return "《我是B的返回》";
    }
}
public class 回调函数 {//测试类，非回调函数
   public static void main(String[] args) {
       ClassA a = new ClassA();
       a.a();
   }
}
*/
/*
执行结果：

执行了a方法
我执行了b
B:我开始调用A的回调-->
A:我就是A的回调函数！我打印：《我是B传的参数》
B:我收到了回调的结果:A:我就是A的回调函数！我打印：《我是B传的参数》的返回。<--我完成调用A的回调
A:执行完得到的结果:《我是B的返回》

7.第6个版本
先声明回调函数，再使用
*/
interface Interface {
    public String backs(String n);
}
class ClassA {
    public void a() {
        System.out.println("执行了a方法");
        Interface in = (n -> {
            System.out.println("A：我是直接使用回调接口，我接收的参数是：" + n);
            return "我是回调的返回数据";
        });

        String backs = in.backs("我A，我是《in》的使用者");
        System.out.println("backes:" + backs);
    }
}

//调用rrrrrr
public class 回调函数 {//测试类，非回调函数
   public static void main(String[] args) {
       ClassA a = new ClassA();
       a.a();
   }
}
/*
执行结果

执行了a方法
A：我是直接使用回调接口，我接收的参数是：我A，我是《in》的使用者
backes:我是回调的返回数据

*/
